/*****************************************************************************
 * kal_freertos_thread.c
 *
 * Copyright (C) 2019 Jeasonvor <1101627719@qq.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/

#include "plum_api.h"
#include "kal/source/kal_freertos/kal_freertos.h"
/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_DEBUG
#define LOG_MODULE    "kal.thread"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef struct plum_kal_thread {
    plum_s8      name[PLUM_KAL_THREAD_NAME_MAX]; /* 线程名称 */
    TaskHandle_t hdl;                            /* 线程句柄 */
    plum_void (*func)(plum_void *arg);           /* 执行函数 */
    plum_void *      arg;                        /* 参数     */
    struct list_head list;                       /* 链表     */
} kal_thread_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

PLUM_PRIVATE kal_thread_t thread_head;

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
plum_s32 kal_thread_add(kal_thread_t *node)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!node) {
            LOG_E("kal_thread_add param err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        list_add(&node->list, &thread_head.list);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
kal_thread_t *kal_thread_find(PLUM_CONST plum_s8 *name)
{
    struct list_head *pos;
    list_for_each(pos, &thread_head.list)
    {
        kal_thread_t *node = list_entry(pos, kal_thread_t, list);
        if (strcmp((const char *)node->name, (const char *)name) == 0) {
            return (node);
        }
    }

    return (plum_null);
}

PLUM_PRIVATE
plum_s32 kal_thread_del(kal_thread_t *node)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!node) {
            LOG_E("kal_thread_del param err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        rc = eTaskGetState(node->hdl);
        if ((rc != eDeleted) && (rc != eInvalid)) {
            vTaskDelete(node->hdl);
        }

        list_del(&node->list);
        KAL_FREERTOS_FREE(node);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
kal_thread_t *kal_thread_apply(PLUM_CONST plum_s8 *name, TaskHandle_t hdl)
{
    kal_thread_t *node;

    do {
        if (!thread_head.list.next) {
            INIT_LIST_HEAD(&thread_head.list);
        }

        node = kal_thread_find(name);
        if (!node) {
            node = (kal_thread_t *)KAL_FREERTOS_MALLOC(sizeof(kal_thread_t));
            if (!node) {
                LOG_E("kal_thread_apply malloc err");
                break;
            }
            snprintf((char *)node->name, sizeof(node->name), "%s", name);
            node->hdl = hdl;

            INIT_LIST_HEAD(&node->list)
        }
        else {
            LOG_E("System have registered the thread,named:%s", name);
        }
    } while (0);

    return (node);
}

PLUM_PRIVATE
plum_void kal_thread_main(plum_void *arg)
{
    kal_thread_t *node = (kal_thread_t *)arg;

    if (node->func) {
        node->func(node->arg);
    }

    LOG_I("thead[%s] has auto exit", node->name);

    vTaskDelete(node->hdl);
    kal_thread_del(node);
}
/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_kal_thread_create(PLUM_CONST plum_s8 *name,
                                plum_void (*task)(plum_void *arg),
                                plum_void *                arg,
                                plum_kal_thread_priority_t priority,
                                plum_u32                   stack_sz)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        /* stack depth */
        plum_u32 stack_depth = stack_sz / sizeof(StackType_t);

        kal_thread_t *node = kal_thread_apply(name, 0);
        if (!node) {
            LOG_E("kal_thread_apply err");
            rc = PLUM_ECODE_EMEM;
            break;
        }

        plum_s32 rc = xTaskCreate(kal_thread_main, name, stack_depth, node,
                                  priority, &node->hdl);
        if (rc != pdPASS) {
            LOG_E("xTaskCreate err,rc:%d", rc);
            rc = PLUM_ECODE_EIO;
        }

        node->func = task;
        node->arg  = arg;
        rc         = kal_thread_add(node);
        if (rc) {
            LOG_E("plum_kal_thread_add err,rc:%d", rc);
            break;
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_delete(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        kal_thread_t *node = kal_thread_find(name);
        if (!node) {
            LOG_E("The thread not find,named:%s", name);
            rc = PLUM_ECODE_EPARA;
            break;
        }

        rc = kal_thread_del(node);
        if (rc) {
            LOG_E("kal_thread_del err,rc:%d", rc);
            break;
        }
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_suspend(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        kal_thread_t *node = kal_thread_find(name);
        if (!node) {
            LOG_E("The thread not find,named:%s", name);
            rc = PLUM_ECODE_EPARA;
            break;
        }

        vTaskSuspend(node->hdl);
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_kal_thread_resume(PLUM_CONST plum_s8 *name)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        kal_thread_t *node = kal_thread_find(name);
        if (!node) {
            LOG_E("The thread not find,named:%s", name);
            rc = PLUM_ECODE_EPARA;
            break;
        }

        vTaskResume(node->hdl);
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_void plum_kal_thread_show(PLUM_CONST plum_s8 *name)
{
    if (name) {
        kal_thread_t *node = kal_thread_find(name);
        if (node) {
            LOG_D("thread [%p] named:%s", node->hdl, node->name);
        }
    }
    else {
        struct list_head *pos;
        list_for_each(pos, &thread_head.list)
        {
            kal_thread_t *node = list_entry(pos, kal_thread_t, list);
            LOG_D("thread [%p] named:%s", node->hdl, node->name);
        }
    }
}

PLUM_PUBLIC
plum_void plum_kal_thread_delay(plum_u32 ms)
{
    vTaskDelay(ms);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
